package pl.edu.agh.intobl;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.uncommons.maths.random.MersenneTwisterRNG;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.CandidateFactory;
import org.uncommons.watchmaker.framework.EvolutionEngine;
import org.uncommons.watchmaker.framework.EvolutionObserver;
import org.uncommons.watchmaker.framework.EvolutionaryOperator;
import org.uncommons.watchmaker.framework.FitnessEvaluator;
import org.uncommons.watchmaker.framework.GenerationalEvolutionEngine;
import org.uncommons.watchmaker.framework.PopulationData;
import org.uncommons.watchmaker.framework.SelectionStrategy;
import org.uncommons.watchmaker.framework.factories.StringFactory;
import org.uncommons.watchmaker.framework.operators.EvolutionPipeline;
import org.uncommons.watchmaker.framework.operators.StringCrossover;
import org.uncommons.watchmaker.framework.operators.StringMutation;
import org.uncommons.watchmaker.framework.selection.TournamentSelection;
import org.uncommons.watchmaker.framework.termination.TargetFitness;

public class Evolution {

	public static char[] chars;

	static {
		// Define the set of permitted characters (A-Z plus space).
		chars = new char[27];
		for (char c = 'A'; c <= 'Z'; c++) {
			chars[c - 'A'] = c;
		}
		chars[26] = ' ';
	}

	public static void main(String[] args) {
		EvolutionEngine<String> engine = new GenerationalEvolutionEngine<String>(
				getCandidateFactory(), getEvolutionaryOperator(),
				getFitnessEvaluator(), getSelectionStrategy(),
				getRandomNumberGenerator());

		engine.addEvolutionObserver(new EvolutionObserver<String>() {
			public void populationUpdate(PopulationData<? extends String> data) {
				System.out.printf("Generation %d: %s\n",
						data.getGenerationNumber(), data.getBestCandidate());
			}
		});

		String result = engine.evolve(10, 0, new TargetFitness(11, true));
		System.out.println(result);
	}

	public static CandidateFactory<String> getCandidateFactory() {
		// Factory for random 11-character Strings.
		return new StringFactory(chars, 11);
	}

	public static EvolutionaryOperator<String> getEvolutionaryOperator() {
		List<EvolutionaryOperator<String>> operators = new LinkedList<EvolutionaryOperator<String>>();
		operators.add(new StringCrossover());
		operators.add(new StringMutation(chars, new Probability(0.02)));

		return new EvolutionPipeline<String>(operators);
	}

	public static FitnessEvaluator<String> getFitnessEvaluator() {
		return new StringEvaluator();
	}

	public static SelectionStrategy<Object> getSelectionStrategy() {
		return new TournamentSelection(new Probability(0.85));
	}

	public static Random getRandomNumberGenerator() {
		return new MersenneTwisterRNG();
	}

}
